#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "Render/Renderer.h"

#include "Render/VertexBuffer.h"
#include "Render/VertexBufferLayout.h"
#include "Render/IndexBuffer.h"
#include "Render/VertexArray.h"
#include "Render/Shader.h"
#include "Render/Texture.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/string_cast.hpp"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"


#include "ApplicationEvent.h"
#include "KeyEvent.h"
#include "TestClearColor.h"
#include "Testcube.h"
#include "TestCubeTex.h"
#include "TestCamera.h"
#include "TestLight.h"
#include "Game1.h"
#include "ResourceManager.h"

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main() {
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);



    // glfw window creation
    // --------------------
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    /**
     * 交换间隔，交换缓冲区之前等待的帧数，通常称为v-sync
     * 默认情况下，交换间隔为0
     * 这里设置为1，即每帧更新一次
     **/
    glfwSwapInterval(1);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    unsigned char *glVersion;
    GLCall(glVersion = (unsigned char *) glGetString(GL_VERSION));
    std::cout << "Status: Using GL " << glVersion << std::endl;
    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    {


//        glEnable(GL_CULL_FACE);
//        glEnable(GL_BLEND);
//        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);




        Renderer renderer;
        // IMGUI Begin============================


        test::Test* currentTest = nullptr;
        test::TestMenu* testMenu = new test::TestMenu(currentTest,window);
        currentTest = testMenu;
        //std::bind();
        glfwSetWindowUserPointer(window, &testMenu);
        glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int width, int height)
        {
            test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);


            WindowResizeEvent event(width, height);
            data->OnEvent(event);
        });
        glfwSetWindowCloseCallback(window, [](GLFWwindow* window)
        {
            test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);
            WindowCloseEvent event;

            data->OnEvent(event);
        });
        glfwSetScrollCallback(window, [](GLFWwindow* window, double xOffset, double yOffset)
        {
            ImGuiIO& io = ImGui::GetIO();
            if(io.WantCaptureMouse)
            {
                return ;
            }
            test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);

            MouseScrolledEvent event((float)xOffset, (float)yOffset);
            data->OnEvent(event);
        });
        glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos)
        {
            test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);

            MouseMovedEvent event((float)xpos, (float)ypos);
            data->OnEvent(event);

        });
        glfwSetMouseButtonCallback(window,[](GLFWwindow* window, int button, int action, int mods)
       {
           test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);
           ImGuiIO& io = ImGui::GetIO();
           if(io.WantCaptureMouse)
           {
               return ;
           }
           switch (action)
           {
               case GLFW_PRESS:
               {
                   MouseButtonPressedEvent event(button);
                   data->OnEvent(event);
                   break;
               }
               case GLFW_RELEASE:
               {
                   MouseButtonReleasedEvent event(button);
                   data->OnEvent(event);
                   break;
               }

           }

       });



        glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
        {
            test::TestMenu* data = *(test::TestMenu**)glfwGetWindowUserPointer(window);
            ImGuiIO& io = ImGui::GetIO();
            if(io.WantCaptureKeyboard)
            {
                return ;
            }
            switch (action)
            {
                case GLFW_PRESS:
                {
                    KeyPressedEvent event(key, 0);
                    data->OnEvent(event);
                    break;
                }
                case GLFW_RELEASE:
                {
                    KeyReleasedEvent event(key);
                    data->OnEvent(event);
                    break;
                }
                case GLFW_REPEAT:
                {
                    KeyPressedEvent event(key, 1);
                    data->OnEvent(event);
                    break;
                }
            }
        });
//=======================================================================================================================

        testMenu->RegisterTest<test::TestClearColor>("ClearColor");
        testMenu->RegisterTest<test::Testcube>("TestCube");
        testMenu->RegisterTest<test::TestCubeTex>("TestCubeTex");
        testMenu->RegisterTest<test::TestCamera>("TestCamera");
        testMenu->RegisterTest<test::TestLight>("TestLight");
        testMenu->RegisterTest<test::Game1>("Game1");
        //TestCubeTex
        /* Loop until the user closes the window */
        ImGui::CreateContext();

        ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui::StyleColorsDark();

        // 需要指定glsl版本, 也就是shader中的version
        const char* glsl_version = "#version 330";
        ImGui_ImplOpenGL3_Init(glsl_version);
        // IMGUI Begin============================
        double lasttime = glfwGetTime();

        GLCall(glEnable(GL_BLEND));
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
        while (!glfwWindowShouldClose(window))
        {
            /* Render here */
            GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
            renderer.Clear();

            ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplGlfw_NewFrame();
            ImGui::NewFrame();
            double curtime = glfwGetTime();
            double deltatime = curtime - lasttime;
            lasttime = curtime;



            if (currentTest)
            {

                currentTest->OnUpdate(deltatime);
                currentTest->OnRender();
                ImGui::Begin("ImGui-Test");
                if (currentTest != testMenu && ImGui::Button("<-"))
                {
                    delete currentTest;
                    currentTest = testMenu;
                }
                currentTest->OnImGuiRender();
                ImGui::End();
            }

            ImGui::Render();
            ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

            /* Swap front and back buffers */
            glfwSwapBuffers(window);

            /* Poll for and process events */
            glfwPollEvents();
        }
        ResourceManager::Delete();
        delete currentTest;
        if (currentTest != testMenu)
        {
            delete testMenu;
        }
    }

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);

    glfwTerminate();
    return 0;

}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

